#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import re
import shlex
import subprocess
import time
from typing import Iterator

from saporacle.comm.common_util import logger_watched, run_subprocess
from saporacle.comm.constants import SAPOracleErrorCode
from saporacle.comm.logger import logger
from saporacle.comm.saporacle_exception import SAPOracleException
from saporacle.resource.saporacle import SAPOracle


class DataBackupSAPOracle(SAPOracle):

    """SAP on Oracle 数据备份能力"""

    def __init__(self, oracle_sid: str, sapsid: str, db_user: str = "", db_pwd: str = "") -> None:
        super().__init__(oracle_sid, sapsid, oracle_sid, sapsid, db_user, db_pwd)

    """brbackup 数据备份"""

    def check_brbackup(self) -> None:
        """
        :raise SAPOracleException:
        """
        tool = self._brtools_location() / "brbackup.exe"
        command = f"{tool} -c force -d util_file -e 0 -k no -l E -m all -o dist,time -q check -t online_cons -u"
        inp = f"{self._db_user}/{self._db_pwd}\n"
        process = run_subprocess(command, inp=inp)
        if "BRBACKUP completed successfully" not in process.stdout:
            logger.error("Check brbackup failed.")
            raise SAPOracleException(SAPOracleErrorCode.SUBPROCESS_EXCEPTION, command, "UnknownOutput")
        logger.info("Check brbackup succeeded.")

    @logger_watched(3)
    def run_brbackup(self) -> Iterator[tuple[float, float]]:
        """
        :raise SAPOracleException:
        """
        tool = self._brtools_location() / "brbackup.exe"
        command = f"{tool} -c force -d util_file -e 0 -k no -l E -m all -o dist,time -t online_cons -u"
        logger.info(command)
        try:
            with self._brtools_outfile().open("w", encoding="utf-8") as f:
                process = subprocess.Popen(shlex.split(command, posix=os.name == "posix"),
                                           stdin=subprocess.PIPE, stdout=f, stderr=subprocess.STDOUT, text=True)
        except Exception as e:
            logger.error(f"Exception occurred while creating brbackup process. {command} {e}", exc_info=1)
            raise SAPOracleException(SAPOracleErrorCode.SUBPROCESS_EXCEPTION, command, "UnknownException")
        process.stdin.write(f"{self._db_user}/{self._db_pwd}\n") and process.stdin.flush()
        while True:
            yield self._brbackup_progress()
            if process.poll() is not None:
                break
            time.sleep(30)
        cont = self._brtools_outfile().read_text(encoding="utf-8")
        if "BRBACKUP completed successfully" not in cont:
            logger.error("BRBACKUP not completed successfully.")
            raise SAPOracleException(SAPOracleErrorCode.SUBPROCESS_EXCEPTION, command, "UnknownOutput")
        logger.info("BRBACKUP completed successfully.")

    def terminate_brbackup(self) -> None:
        """
        :raise SAPOracleException:
        """
        tool = self._brtools_location() / "brbackup.exe"
        command = f"{tool} -c force -g term"
        process = run_subprocess(command)
        if "completed successfully" not in process.stdout:
            logger.error(f"Brbackup terminated Failed. {process.stdout}")
            raise SAPOracleException(SAPOracleErrorCode.SUBPROCESS_EXCEPTION, command, "UnknownOutput")
        logger.info("Brbackup terminated successfully.")

    """private"""

    def _brbackup_progress(self) -> tuple[float, float]:
        """
        :raise SAPOracleException:
        """
        file = self._brtools_outfile()
        if not file.exists():
            logger.error(f"Brbackup outfile not exists. {file.absolute()}")
            raise SAPOracleException(SAPOracleErrorCode.UNKNOWN_ERROR)
        cont = file.read_text(encoding="utf-8")
        matches = re.findall(r"BR0061I.+size ([.\d]+) MB", cont)
        if not matches:
            logger.warn(f"Cannot match brbackup total size.")
        total_size = sum((float(m) for m in matches), 0.0)
        matches = re.findall(r"#SAVED.+#(\d+)", cont)
        if not matches:
            logger.warn(f"Cannot match brbackup saved size.")
        trans_size = sum((int(m) / 1024 / 1024 for m in matches), 0.0)
        return trans_size, total_size

